/*********************************************************
          File Name:16.16.h
          Author: Abby Cin
          Mail: abbytsing@gmail.com
          Created Time: Sun 13 Dec 2015 12:09:29 PM CST
**********************************************************/

#ifndef VEC_H_
#define VEC_H_

#include <memory>

template<typename T>
class Vec
{
        private:
                std::allocator<T> alloc;
                void chk_n_alloc()
                {
                        if(size() == capacity())
                                reallocate();
                }
                std::pair<T, T> 
                alloc_n_copy(const T *, const T *);

                void free();
                void reallocate();
                T *elements;
                T *first_free;
                T *cap;

        public:
                Vec(): elements(nullptr), first_free(nullptr), cap(nullptr) { }
                Vec(const T&);  // copy constructor function
                Vec &operator=(const Vec<T> &);
                ~Vec();

                void push_back(const T &);
                size_t size() const;
                size_t capacity() const;
                T *begin() const;
                T *end() const;
};

template<typename T>
Vec<T>::Vec(const T &n)
{
        auto newdata = alloc_n_copy(n.begin(), n.end());
        elements = newdata.first;
        first_free = newdata.second;
        cap = newdata.second;
}

template<typename T>
Vec<T>& Vec<T>::operator=(const Vec<T> &rhs)
{
        auto data = alloc_n_copy(rhs.begin(), rhs.end());
        free();
        elements = data.fisrt;
        first_free = data.second;
        cap = data.second;

        return *this;
}

template<typename T>
Vec<T>::~Vec()
{
        free();
}

template<typename T>
inline std::pair<T, T> Vec<T>::alloc_n_copy(const T *lhs, const T *rhs)
{
       auto data = alloc.allocate(rhs - lhs);
       return {data, uninitialized_copy(lhs, rhs, data)};
}

template<typename T>
inline void Vec<T>::free()
{
        if(elements)
        {
                for(auto p = first_free; p != elements;)
                        alloc.destroy(--p);
                alloc.deallocate(elements, cap - elements);
        }
}

template<typename T>
inline void Vec<T>::reallocate()
{
        auto newcap = size() ? 2 * size() : 1;

        auto newdata = alloc.allocate(newcap);

        auto dest = newdata;
        auto elem = elements;

        for(size_t i = 0; i != size(); ++i)
                alloc.construct(dest++, std::move(*elem++));

        free();

        elements = newdata;
        first_free = dest;
        cap = elements + newcap;
}

// implementation of public member functions

template<typename T>
inline void Vec<T>::push_back(const T &rhs)
{
        chk_n_alloc();
        alloc.construct(first_free++, rhs);
}

template<typename T>
inline size_t Vec<T>::size() const
{
        return first_free - elements;
}

template<typename T>
inline size_t Vec<T>::capacity() const
{
        return cap - elements;
}

template<typename T>
inline T *Vec<T>::begin() const
{
        return elements;
}

template<typename T>
inline T *Vec<T>::end() const
{
        return first_free;
}

#endif
